package com.meshnetics.gb.util;

import java.nio.ByteBuffer;
import java.math.BigInteger;

/**
 * Utility class for for various common operations.
 *
 * @author Yury Aserchev (yaserchev@meshnetics.com)
 */
public final class Utils {
    public static final int RADIX_DEC = 10;
    public static final int RADIX_HEX = 16;

    /**
     * Converts a string hex representation of a big number to array of bytes.
     *
     * @param str given string.
     * @return parsed byte array.
     *
     * @throws  NumberFormatException   if input is not a correct string of hex bytes.
     */
    public static byte[] toByteArray(String str) {
        str = str.replace(" ", "");
        int len = str.length();

        if (len == 0) {
            return new byte[0];
        }

        if ((len & 0x01) != 0) {
            throw new NumberFormatException("must be even number of hex digits");
        }
        byte[] ret = new byte[(len >> 1)];
        int i = 0;
        int j = 2;
        int n = 0;

        while (i < len) {
            ret[n++] = (byte) Integer.parseInt(str.substring(i, j).toLowerCase(), 16);
            i = j;
            j += 2;
        }
        return ret;
    }

    /**
     * Parse given string using BigInteger class. It works correctly with
     * big int values in decimal (without any prefix) and hex (with '0x'
     * prefix) formats.
     * @param str given string.
     * @return parsed long.
     */
    public static long parseLongString(final String str) {
        if (str == null) {
            throw new NullPointerException("parameter str is null");
        }
        final String tmp;
        final int radix;
        if (str.startsWith(Conversions.HEX_PREFIX)) {
            tmp = str.substring(Conversions.HEX_PREFIX.length());
            radix = RADIX_HEX;
        } else {
            tmp = str;
            radix = RADIX_DEC;
        }
        return new BigInteger(tmp, radix).longValue();
    }

    /**
     * Parce given String to long using Long.decode() method. <b>Note:</b>
     * Doesn't work correctly with big int values.
     * @param string given string.
     * @return parsed long.
     */
    public static long decodeString(final String string) {
        if (string == null) {
            throw new NullPointerException("parameter string is null");
        }
        return Long.decode(string).longValue();
    }

    /**
     * Checks if the given object is an instance of required Class type.
     *
     * @param object checking object
     * @param aClass required class type
     * @param name logical name of object
     * @throws NullPointerException if any parameter is null
     * @throws IllegalArgumentException if object is not instance of target class type
     */
    public static void checkClass(final Object object, final Class aClass, final String name){
        if (aClass == null) {
            throw new NullPointerException("parameter aClass is null");
        }
        if (name == null) {
            throw new NullPointerException("parameter name is null");
        }
        if (object == null) {
            throw new NullPointerException("parameter object is null");
        }
        if (!aClass.isInstance(object)){
            throw new IllegalArgumentException(name + " is not instance of " + aClass.getName());
        }
    }

    /**
     * Checks if the given buffer contains at least 'length' bytes after current position
     *
     * @param byteBuffer buffer
     * @param length length
     * @throws NullPointerException if byteBuffer is null
     * @throws IllegalArgumentException if length is less than zero or buffer has not enough bytes
     */
    public static void checkBuffer(final ByteBuffer byteBuffer, final int length){
        if (byteBuffer == null) {
            throw new NullPointerException("parameter byteBuffer is null");
        }
        if (length < 0){
            throw new IllegalArgumentException("length cannot be less than zero, passed: " + length);
        }
        if (byteBuffer.capacity() < byteBuffer.position() + length){
            throw new IllegalArgumentException("expected " + length + " bytes after position " +
                    byteBuffer.position() + ", but capacity is " + byteBuffer.capacity());
        }
    }
}
